#include <linux/init.h>         //包含宏定义得头文件
#include <linux/module.h>       //包含初始化加载模块得头文件
#include <linux/kdev_t.h>       //处理设备号宏定义的头文件
#include <linux/fs.h>           //文件操作集
#include <linux/uaccess.h>      //copy_to_user函数的文件
#include <linux/io.h>           //ioermap等函数的头文件
// #include <linux/miscdevice.h>   //杂项设备头文件
/*包含头文件*/
/*************************************************
*字符设备驱动，杂项设备驱动是字符设备驱动的一种，但是杂项设备驱动的好处是
*不用自己分配设备主设备号固定是10次设备号也可以由系统分配，设备节点也是不
*需要手动生成对于字符设备我们需要处理好这两个问题
*（1）分配设备号：主设备号、次设备号（内核提供静态分配与动态分配两种方法）
*（2）设备节点需要挂载   
*************************************************/
/*宏定义*/
#define DEVICE_NUMBER       1   //申请设备号的数量
#define DEVICE_SNAME "schrdev"  
#define DEVICE_ANAME "achrdev"  //设备的名称
#define DEVICE_MINOR_NUMBER 0   //次设备号的起始地址
#define GPIO5_DR 0x02           //蜂鸣器链接的GPIO地址

/*全局变量*/
static int major_num;//存储主设备号
static int minor_num;//存储次设备号
// unsigned int*vir_gpio5_dr;  //指针的目的是为了存储分配的虚拟地址
// static int b[5];
// static int count;

/*内核传参函数*/
module_param(major_num,int,S_IRUSR);//内核传参目的是为了传递应用层手动分配的主设备号
module_param(minor_num,int,S_IRUSR);//传递次设备号
// module_param_array(b，int,&count,S_IRUSR);

/*功能实现*/
static int chrdev_init(void)
{
    dev_t dev_num;
    int reg;
    if(major_num)//major的值是由应用层传递的
    {
        dev_num = MKDEV(major_num,minor_num);
        reg=register_chrdev_region(dev_num,DEVICE_NUMBER,DEVICE_SNAME);//只是分配设备号并没有注册字符设备下一章节会展示
        if(reg<0)
        {
            printk("chrdev register error!\n");
        }
        printk("chrdev register ok!\n");
    }
    else//动态分配主次设备号
    {
        reg=alloc_chrdev_region(&dev_num,DEVICE_MINOR_NUMBER,DEVICE_NUMBER,DEVICE_ANAME);//动态分配设备号函数
        if(reg<0)
        {
            printk("chrdev register error!\n");
        }
        printk("chrdev register ok!\n");

        //取出主次设备号并打印出来
        major_num=MAJOR(dev_num);
        minor_num=MINOR(dev_num);
        printk("chrdev major_num is:%n\n",major_num);
        printk("chrdev minor_num is:%n\n",minor_num);
    }
    return 0;
    //映射地址在init函数内这样加载上驱动就映射完成了
    // vir_gpio5_dr=ioermap();
}

static void chrdev_exit(void)//退出函数没有返回值
{
    unregister_chrdev_region(MKDEV(major_num,minor_num),DEVICE_NUMBER);//注销申请的字符设备号
    printk("byb byb !"); 
}     


module_init(chrdev_init);
module_exit(chrdev_exit); 
/*驱动的出口与入口函数都在此处*/
MODULE_LICENSE("GPL");
/*申明信息*/
	
/*注意在开发板中加载驱动的时候一定要保持板卡的内核和编译的内核是一致的否则会报错
*查看设备节点：ls /dev/hello_misc
*/

